#include "mutmap.h"
#include "mutmodel.h"

CMUTTexManager g_mapTexManager;

LPD3DXEFFECT g_grassEffect = 0;
int g_grassEffectCount = 0;
char g_grassEffectData[] = "\
sampler t0 : register( s0 );\
sampler t1 : register( s1 );\
sampler sm : register( s5 );\
sampler sm2 : register( s6 );\
matrix g_matWorld;\
matrix g_matView;\
matrix g_matProjection;\
matrix g_matLightViewProj;\
matrix g_matLightViewProjSmall;\
float g_time;\
struct VSOUTPUT{\
float4 vPosition : POSITION0;\
float2 vTexCoord : TEXCOORD0;\
float4 vWorldPos : TEXCOORD1;\
};\
VSOUTPUT VSMain( float3 vPosition : POSITION, float3 normal : NORMAL0, float2 vTexCoord : TEXCOORD0 ){\
	VSOUTPUT Out = (VSOUTPUT)0;\
	Out.vWorldPos = mul( float4( vPosition, 1.0f ), g_matWorld );\
	Out.vPosition = mul( mul( Out.vWorldPos, g_matView ), g_matProjection );\
	Out.vTexCoord = vTexCoord;\
	Out.vWorldPos.w = saturate( saturate( normalize( normal ).y*100.0f )  + 0.8f ) ;\
	return Out;\
}\
float2 shadowOffset = float2( 1.0f / 1024.0f, 1.0f / 1024.0f );\
float4 lightOrthoCoord( float3 vWorldPos, matrix matLightViewProj ){\
	float4 orthoPos = mul( float4( vWorldPos, 1.0f ), matLightViewProj );\
	orthoPos /= orthoPos.w;\
	orthoPos.x = ( orthoPos.x + 1.0f ) * 0.5f;\
	orthoPos.y = ( 1.0f - orthoPos.y ) * 0.5f;\
	return orthoPos;\
}\
float shadowPCF( sampler s, float4 orthoPos ){\
	float read = tex2D( s, orthoPos.xy ).r;\
	float d = saturate( 1e10 * ( read - orthoPos.z ) );\
	return saturate( read + d + 0.2f );\
}\
bool g_bShadowed;\
float4 PSMain( float2 vTexCoord : TEXCOORD0, float4 vWorldPos : TEXCOORD1 ) : COLOR0{\
	float4 aColor = tex2D( t0, vTexCoord );\
	float4 dColor = tex2D( t1, vTexCoord );\
	float4 orthoPos = lightOrthoCoord( vWorldPos.xyz, g_matLightViewProj );\
	float shadow = 1.0f;\
	if ( g_bShadowed ){\
		if ( orthoPos.x <= 0.0f || orthoPos.x >= 1.0f || orthoPos.y <= 0.0f || orthoPos.y >= 1.0f )\
			shadow = shadowPCF( sm2, lightOrthoCoord( vWorldPos.xyz, g_matLightViewProjSmall ) );\
		else\
			shadow = shadowPCF( sm, orthoPos );\
	}\
	float4 finalColor = float4( 0.4f*dColor.r, 0.6f*dColor.g, 0.2f*dColor.b, saturate( saturate( aColor.r - 0.275f ) * 1000.0f ) * ( 1.0f - saturate( vTexCoord.y - 0.8f )*4.0f ) );\
	finalColor.rgb *= shadow;\
	return finalColor;\
}\
float4 PSLeaf( float2 vTexCoord : TEXCOORD0 ) : COLOR0{\
	float4 aColor = tex2D( t0, vTexCoord );\
	float4 dColor = tex2D( t1, vTexCoord );\
	return float4( float3( 0.4f, 0.6f, 0.2f ) * dColor.xyz, aColor.r );\
}\
VSOUTPUT VSTrunk( float3 vPosition : POSITION, float3 normal : NORMAL0, float2 vTexCoord : TEXCOORD0 ){\
	VSOUTPUT Out = (VSOUTPUT)0;\
	Out.vWorldPos = mul( float4( vPosition, 1.0f ), g_matWorld );\
	Out.vPosition = mul( mul( Out.vWorldPos, g_matView ), g_matProjection );\
	Out.vTexCoord = vTexCoord;\
	Out.vWorldPos.w = saturate( saturate( dot( normalize( normal ), normalize( float3( 0,1,1 ) ) ) ) + 0.2f ) ;\
	return Out;\
}\
float4 PSTrunk( float2 vTexCoord : TEXCOORD0, float4 vWorldPos : TEXCOORD1 ) : COLOR0{\
	float4 orthoPos = lightOrthoCoord( vWorldPos.xyz, g_matLightViewProj );\
	float shadow = 1.0f;\
	if ( orthoPos.x <= 0.0f || orthoPos.x >= 1.0f || orthoPos.y <= 0.0f || orthoPos.y >= 1.0f )\
		shadow = shadowPCF( sm2, lightOrthoCoord( vWorldPos.xyz, g_matLightViewProjSmall ) );\
	else\
		shadow = shadowPCF( sm, orthoPos );\
	float light = min( vWorldPos.w, shadow );\
	return light * tex2D( t0, vTexCoord );\
}\
technique techGrass{\
	pass p0{\
		VertexShader = compile vs_2_0 VSMain();\
		PixelShader = compile ps_3_0 PSMain();\
		AlphaBlendEnable = True;\
		SrcBlend = SrcAlpha;\
		DestBlend = InvSrcAlpha;\
		AlphaTestEnable = True;\
		AlphaFunc = GreaterEqual;\
		AlphaRef = 1;\
		CullMode = None;\
	}\
	pass p1{\
		VertexShader = compile vs_2_0 VSMain();\
		PixelShader = compile ps_2_0 PSLeaf();\
		AlphaBlendEnable = False;\
		AlphaTestEnable = True;\
		AlphaFunc = GreaterEqual;\
		AlphaRef = 70;\
		CullMode = None;\
	}\
}\
technique techTreeTrunk{\
	pass p0{\
		VertexShader = compile vs_2_0 VSTrunk();\
		PixelShader = compile ps_3_0 PSTrunk();\
	}\
}\
";
struct GRASSVERTEX{
	D3DXVECTOR3 pos;
	D3DXVECTOR3 normal;
	D3DXVECTOR2 uv;
	static DWORD FVF;
};
DWORD GRASSVERTEX::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;	
HRESULT CMUTGrass::Create( HANDLE hEvent, int gnum ){

	numGrass = gnum;
	if ( numGrass ){
		MUTRequestVertexBuffer( hEvent, 8 * numGrass * sizeof( GRASSVERTEX ), GRASSVERTEX::FVF, vb );
		MUTRequestIndexBuffer( hEvent, 3 * 4 * numGrass * sizeof( WORD ), D3DFMT_INDEX16, ib );
		atex = g_mapTexManager.GetTexture( L"grassblade/blade.jpg", hEvent );
		dtex = g_mapTexManager.GetTexture( L"grassblade/blade_diffuse.jpg", hEvent );
	}

	return S_OK;
}

void CMUTGrass::InitData( D3DXVECTOR3 bias, D3DXVECTOR3* pts ){
	int index = 0;
	float s = 60.0f;
	float h = 2*s;
	float bottomScale = 0.5f;

	GRASSVERTEX* verts = (GRASSVERTEX*)vertsVoid;
	
	for ( int i = 0; i < numGrass; i++, index += 8 ){
		float x = pts[i].x+bias.x;
		float z = pts[i].z+bias.z;
		float y = pts[i].y;
		float rs = s * ( rand() % 250 + 750 ) / 1000.0f;
		float start = rand() % 10000 / 1000.0f;
		for ( int k = 0; k < 4; k++ ){
			float sx = rs * cos( 3.1415f * k*0.25f + start );
			float sz = rs * sin( 3.1415f * k*0.25f + start );
			verts[ index+2*k ].pos = D3DXVECTOR3( x - sx, y + h*(k%2), z - sz );
			verts[ index+2*k+1 ].pos = D3DXVECTOR3( x + sx, y + h*(k%2), z + sz );
		}
		for ( int k = 0; k < 8; k++ ){
			verts[ index+k ].normal = D3DXVECTOR3( 0, 1, 0 );
		}

		verts[ index ].uv = D3DXVECTOR2( 0, 1 );
		verts[ index+1 ].uv = D3DXVECTOR2( 1, 1 );
		verts[ index+2 ].uv = D3DXVECTOR2( 0, 0.1f );
		verts[ index+3 ].uv = D3DXVECTOR2( 1, 0.1f );
		verts[ index+4 ].uv = D3DXVECTOR2( 0, 1 );
		verts[ index+5 ].uv = D3DXVECTOR2( 1, 1 );
		verts[ index+6 ].uv = D3DXVECTOR2( 0, 0.1f );
		verts[ index+7 ].uv = D3DXVECTOR2( 1, 0.1f );
	}

	WORD* tris = (WORD*)trisVoid;

	index = 0;
	for ( int i = 0; i < numGrass * 2; i++, index += 6 ){
		tris[ index ] = i*4 ;
		tris[ index+1 ] = i*4 + 2 ;
		tris[ index+2 ] = i*4 + 1 ;
		tris[ index+3 ] = i*4 + 2 ;
		tris[ index+4 ] = i*4 + 3 ;
		tris[ index+5 ] = i*4 + 1 ;
	}

}

int g_grassdrawn = 0;
void CMUTGrass::Render( LPDIRECT3DDEVICE9 pDevice ){
	if ( !numGrass || !vb || !ib ) return;
	
	pDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	pDevice->SetTexture( 0, *atex );
	pDevice->SetTexture( 1, *dtex );
	pDevice->SetFVF( GRASSVERTEX::FVF );
	pDevice->SetStreamSource( 0, vb, 0, sizeof( GRASSVERTEX ) );
	pDevice->SetIndices( ib );
	pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, 8 * numGrass, 0, 4 * numGrass );

	g_grassdrawn+=numGrass;
}

void CMUTGrass::Release( HANDLE hEvent ){
	if ( hEvent ){
		if ( vb )
			MUTRequestRelease( hEvent, MUTRES_VB, vb );
		if ( ib )
			MUTRequestRelease( hEvent, MUTRES_IB, ib );
	}else{
		if ( vb )
			vb->Release();
		if ( ib )
			ib->Release();
	}
	vb = 0;
	ib = 0;
	if ( atex )
		atex->Release( hEvent );
	atex = 0;
	if ( dtex )
		dtex->Release( hEvent );
	dtex = 0;

	numGrass = 0;
}

////////////////////////////////////////////////////////////////////////////////////

struct WATERVERTEX{
	D3DXVECTOR3 pos;
	D3DXVECTOR2 uv;
	static DWORD FVF;
};
DWORD WATERVERTEX::FVF = D3DFVF_XYZ|D3DFVF_TEX1;
HRESULT CMUTWater::Create(){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	HRESULT hr = S_OK;
	if ( FAILED( hr = pDevice->CreateVertexBuffer( 4 * sizeof(WATERVERTEX), 0, WATERVERTEX::FVF, D3DPOOL_MANAGED, &vb, NULL ) ) ){
		MUTDXErrorTrace( hr, L"water vb failed" );
		return E_FAIL;
	}
	if ( FAILED( hr = pDevice->CreateIndexBuffer( 4 * sizeof( WORD ), 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &ib, NULL ) ) ){
		MUTDXErrorTrace( hr, L"water ib failed" );
		return E_FAIL;
	}

	return S_OK;
}

void CMUTWater::InitData( D3DXVECTOR3 minPos, D3DXVECTOR3 maxPos ){
	WATERVERTEX* verts = 0;
	vb->Lock( 0, 0, (void**)&verts, 0 );
	verts[ 0 ].pos = minPos;
	verts[ 1 ].pos = D3DXVECTOR3( minPos.x, minPos.y, maxPos.z );
	verts[ 2 ].pos = D3DXVECTOR3( maxPos.x, minPos.y, minPos.z );
	verts[ 3 ].pos = maxPos;
	verts[ 0 ].uv = D3DXVECTOR2( 0, 1 );
	verts[ 1 ].uv = D3DXVECTOR2( 0, 0 );
	verts[ 2 ].uv = D3DXVECTOR2( 1, 1 );
	verts[ 3 ].uv = D3DXVECTOR2( 1, 0 );
	vb->Unlock();
	WORD* tris = 0;
	ib->Lock( 0, 0, (void**)&tris, 0 );
	for ( int i = 0; i < 4; i++ )
		tris[i] = i;
	ib->Unlock();
}

void CMUTWater::Render( LPDIRECT3DDEVICE9 pDevice ){
	pDevice->SetFVF( WATERVERTEX::FVF );
	pDevice->SetStreamSource( 0, vb, 0, sizeof( WATERVERTEX ) );
	pDevice->SetIndices( ib );
	pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, 0, 4, 0, 2 );
}

void CMUTWater::Release(){
	if ( vb )
		vb->Release();
	vb = 0;
	if ( ib )
		ib->Release();
	ib = 0;
}

///////////////////////////////////////////////////////////////////////////////////
LPD3DXEFFECT g_mapEffect = 0;
int g_chunksdrawn = 0;

D3DVERTEXELEMENT9 g_elem[] = {
/*	stream, offset, Type,				Method, Usage,					UsageIndex */
	{0,		0,		D3DDECLTYPE_FLOAT3, 0,		D3DDECLUSAGE_POSITION,	0 },
	{0,		12,		D3DDECLTYPE_FLOAT2, 0,		D3DDECLUSAGE_TEXCOORD,	0 },
	{0,		20,		D3DDECLTYPE_FLOAT2, 0,		D3DDECLUSAGE_TEXCOORD,  1 },
	{0,		28,		D3DDECLTYPE_FLOAT3, 0,		D3DDECLUSAGE_NORMAL,	0 },
	D3DDECL_END()
};
LPDIRECT3DVERTEXDECLARATION9 g_mapvdecl = NULL;
int g_mapvdeclCount = 0;

struct MAPVERTEX{
	D3DXVECTOR3 pos;
	D3DXVECTOR2 uv;
	D3DXVECTOR2 uvBlend;
	D3DXVECTOR3 normal;
};

HRESULT CMUTMapChunk::Create( WCHAR* blendfn, HANDLE hEvent, int _gnum, int _tnum ){
	if ( !g_mapvdecl || !vb || !ib ){

		if ( !g_mapvdeclCount )
			MUTRequestVertexDeclaration( hEvent, g_elem, g_mapvdecl );
	
		if ( !vb ){
			vnum = (MAPCHUNK_VSIZE - 1) * (MAPCHUNK_VSIZE - 1) * 5;
			MUTRequestVertexBuffer( hEvent, vnum * sizeof( MAPVERTEX ), 0, vb );
			g_mapvdeclCount++;
			tex[0] = g_mapTexManager.GetTexture( L"maps/grass.jpg", hEvent );
			tex[1] = g_mapTexManager.GetTexture( L"maps/rock.jpg", hEvent );
			tex[2] = g_mapTexManager.GetTexture( L"maps/road.jpg", hEvent );
			blend = g_mapTexManager.GetTexture( blendfn, hEvent );
			//water.Create( hEvent );
		}

		if ( !ib ){
			fnum = ( MAPCHUNK_VSIZE-1 )*( MAPCHUNK_VSIZE-1 )*( 4+2 );
			MUTRequestIndexBuffer( hEvent, 3 * fnum * sizeof( WORD ), D3DFMT_INDEX16, ib );
		}

	}
	gnum = _gnum;
	grass.Release( hEvent );
	grass.Create( hEvent, gnum );

	tnum = _tnum;
	treeSet.Release( hEvent );
	treeSet.Create( hEvent, tnum );

	return S_OK;
}

void CMUTMapChunk::InitGrass( D3DXVECTOR3 bias, D3DXVECTOR3* pts ){
	if ( !grassed && !treed ) return;

	if ( grassed ){
		grass.InitData( bias, pts );
	}
	/*
	if ( !treed ) return;
	int treenum = min( cnt, 2 );
	for ( int i = 0; i < treenum; i++ ){
		int r = rand() % ( cnt - i );
		D3DXVECTOR3 tmp = pts[ r + i ];
		pts[ r+i ] = pts[ i ];
		pts[ i ] = tmp;
	}
	treeSet.Create( pts, treenum );*/
}

void CMUTMapChunk::Release(){
	if ( vb ){
		vb->Release();
		if ( --g_mapvdeclCount<=0 ){
			if ( g_mapvdecl )
				g_mapvdecl->Release();
			g_mapvdecl = 0;
		}
	}
	if ( ib )
		ib->Release();
	vb = 0;
	ib = 0;
	if ( blend )
		blend->Release();
	blend = 0;
	for ( int i = 0; i < 4; i++ ){
		if ( tex[ i ] )
			tex[ i ]->Release();
	}
	ZeroMemory( tex, sizeof( tex ) );
	box.Release();
}

void CMUTMapChunk::InitDefaultVertexData( CMUTMapWorld* world, D3DXVECTOR2 uvblendBias, D3DXVECTOR3 posBias, BYTE tiles[MAPTILE_HEIGHTMAP_SIZE][MAPTILE_HEIGHTMAP_SIZE], float height[MAPTILE_HEIGHTMAP_SIZE][MAPTILE_HEIGHTMAP_SIZE], POINT bias ){
	MAPVERTEX* pVertex = ( MAPVERTEX * )pVertexVoid;
	int index = 0;
	box.minPos = posBias;
	box.maxPos = posBias + D3DXVECTOR3( (MAPCHUNK_VSIZE-1)*MAPCHUNK_SIZE, 0, (MAPCHUNK_VSIZE-1)*MAPCHUNK_SIZE );
	box.minPos.y = 1e10;
	box.maxPos.y = -1e10;
	for ( int i = 0; i < MAPCHUNK_VSIZE-1; i++ ){
		for ( int j = 0; j < MAPCHUNK_VSIZE-1; j++ ){
			pVertex[ index ].pos = posBias + D3DXVECTOR3( j*MAPCHUNK_SIZE, height[ bias.y+i*2 ][ bias.x+j*2 ], i*MAPCHUNK_SIZE );
			pVertex[ index+1 ].pos = posBias + D3DXVECTOR3( (j+1)*MAPCHUNK_SIZE, height[ bias.y+i*2 ][ bias.x+j*2+2 ], i*MAPCHUNK_SIZE );
			pVertex[ index+2 ].pos = posBias + D3DXVECTOR3( j*MAPCHUNK_SIZE, height[ bias.y+i*2+2 ][ bias.x+j*2 ], (i+1)*MAPCHUNK_SIZE );
			pVertex[ index+3 ].pos = posBias + D3DXVECTOR3( (j+1)*MAPCHUNK_SIZE, height[ bias.y+i*2+2 ][ bias.x+j*2+2 ], (i+1)*MAPCHUNK_SIZE );
			pVertex[ index+4 ].pos = posBias + D3DXVECTOR3( j*MAPCHUNK_SIZE+MAPCHUNK_SIZE/2, height[ bias.y+i*2+1 ][ bias.x+j*2+1 ], i*MAPCHUNK_SIZE+MAPCHUNK_SIZE/2 );
			
			float u = int(tiles[i][j]%4)*0.25f;
			float v = int(tiles[i][j]/4)*0.25f;
			
			pVertex[ index ].uv = D3DXVECTOR2( u, v+0.25f );
			pVertex[ index+1 ].uv = D3DXVECTOR2( u+0.25f, v+0.25f );
			pVertex[ index+2 ].uv = D3DXVECTOR2( u, v );
			pVertex[ index+3 ].uv = D3DXVECTOR2( u+0.25f, v );
			pVertex[ index+4 ].uv = D3DXVECTOR2( u+0.125f, v+0.125f );/*
			pVertex[ index ].uv = D3DXVECTOR2( 0, 1 );
			pVertex[ index+1 ].uv = D3DXVECTOR2( 1, 1 );
			pVertex[ index+2 ].uv = D3DXVECTOR2( 0, 0 );
			pVertex[ index+3 ].uv = D3DXVECTOR2( 1, 0 );
			pVertex[ index+4 ].uv = D3DXVECTOR2( 0.5, 0.5 );
*/
			float blendStep = 1.0f / MAPTILE_SIZE / ( MAPCHUNK_VSIZE-1 );
			u = uvblendBias.x + j * blendStep;
			v = uvblendBias.y + ( MAPCHUNK_VSIZE - 2 - i ) * blendStep; 
			pVertex[ index ].uvBlend	= D3DXVECTOR2( u, v+blendStep );
			pVertex[ index+1 ].uvBlend	= D3DXVECTOR2( u+blendStep, v+blendStep );
			pVertex[ index+2 ].uvBlend	= D3DXVECTOR2( u, v );
			pVertex[ index+3 ].uvBlend	= D3DXVECTOR2( u+blendStep, v );
			pVertex[ index+4 ].uvBlend	= D3DXVECTOR2( u+blendStep/2, v+blendStep/2 );

			D3DXVECTOR3 leftN, upN, rightN, bottomN;
			D3DXVECTOR3 v0_v1 = pVertex[ index+1 ].pos - pVertex[ index ].pos;
			D3DXVECTOR3 v0_v2 = pVertex[ index+2 ].pos - pVertex[ index ].pos;
			D3DXVECTOR3 v0_v4 = pVertex[ index+4 ].pos - pVertex[ index ].pos;
			D3DXVECTOR3 v3_v4 = pVertex[ index+4 ].pos - pVertex[ index+3 ].pos;
			D3DXVECTOR3 v3_v1 = pVertex[ index+1 ].pos - pVertex[ index+3 ].pos;
			D3DXVECTOR3 v3_v2 = pVertex[ index+2 ].pos - pVertex[ index+3 ].pos;

			D3DXVec3Cross( &leftN, &v0_v2, &v0_v4 );
			D3DXVec3Cross( &bottomN, &v0_v4, &v0_v1 );
			D3DXVec3Cross( &upN, &v3_v4, &v3_v2 );
			D3DXVec3Cross( &rightN, &v3_v1, &v3_v4 );
			
			D3DXVec3Normalize( &leftN, &leftN );
			D3DXVec3Normalize( &bottomN, &bottomN );
			D3DXVec3Normalize( &upN, &upN );
			D3DXVec3Normalize( &rightN, &rightN );
			
			D3DXVec3Normalize( &pVertex[ index ].normal, &(leftN+bottomN) );
			D3DXVec3Normalize( &pVertex[ index+1 ].normal, &(rightN+bottomN) );
			D3DXVec3Normalize( &pVertex[ index+2 ].normal, &(leftN+upN) );
			D3DXVec3Normalize( &pVertex[ index+3 ].normal, &(rightN+upN) );
			D3DXVec3Normalize( &pVertex[ index+4 ].normal, &(upN+leftN+bottomN+rightN) );

			for ( int k = 0; k < 5; k++ ){
				if ( pVertex[ index+k ].pos.y > box.maxPos.y )
					box.maxPos.y = pVertex[ index+k ].pos.y;
				if ( pVertex[ index+k ].pos.y < box.minPos.y )
					box.minPos.y = pVertex[ index+k ].pos.y;
			}

			index += 5;
		}
	}

	box.SetupBounding();
	
	//grass.InitData( world, box.minPos, box.maxPos );
	/*D3DXVECTOR3 watermin, watermax;
	watermin = box.minPos;
	watermax = box.maxPos;
	watermin.y = 500.0f;
	watermax.y = watermin.y;
	water.InitData( watermin, watermax );*/
}

void CMUTMapChunk::InitDefaultIndexData(){
	WORD* pIndex = ( WORD * )pIndexVoid;
	int index = 0, vbias = 0;
	for ( int i = 0; i < MAPCHUNK_INDEXLOD2FSTART/4; i++ ){
		pIndex[ index++ ] = vbias;
		pIndex[ index++ ] = vbias+4;
		pIndex[ index++ ] = vbias+1;

		pIndex[ index++ ] = vbias+1;
		pIndex[ index++ ] = vbias+4;
		pIndex[ index++ ] = vbias+3;

		pIndex[ index++ ] = vbias+3;
		pIndex[ index++ ] = vbias+4;
		pIndex[ index++ ] = vbias+2;

		pIndex[ index++ ] = vbias+2;
		pIndex[ index++ ] = vbias+4;
		pIndex[ index++ ] = vbias;

		vbias += 5;
	}
	vbias = 0;
	for ( int i = 0; i < MAPCHUNK_INDEXLOD2FNUM/2; i++ ){
		pIndex[ index++ ] = vbias;
		pIndex[ index++ ] = vbias+2;
		pIndex[ index++ ] = vbias+1;

		pIndex[ index++ ] = vbias+1;
		pIndex[ index++ ] = vbias+2;
		pIndex[ index++ ] = vbias+3;
		vbias += 5;
	}

}

void CMUTMapChunk::Culling( D3DXPLANE* p ){
	visible = box.InsideFrustum( p );
}

void CMUTMapChunk::Render( LPDIRECT3DDEVICE9 pDevice ){
	if ( !visible ) return;
	for ( int i = 0 ; i < 4; i++ ){
		if ( tex[ i ] )
			pDevice->SetTexture( i, *tex[i] );
		else
			pDevice->SetTexture( i, NULL );
	}
	
	if ( blend )
		pDevice->SetTexture( 4, *blend );
	else
		pDevice->SetTexture( 4, NULL );
	pDevice->SetSamplerState( 4, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
	pDevice->SetVertexDeclaration( g_mapvdecl );
	pDevice->SetStreamSource( 0, vb, 0, sizeof( MAPVERTEX ) );
	pDevice->SetIndices( ib );
	if ( true )
		pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, vnum, 3*MAPCHUNK_INDEXLOD2FSTART, MAPCHUNK_INDEXLOD2FNUM );
	else
		pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, vnum, 0, MAPCHUNK_INDEXLOD2FSTART );
	g_chunksdrawn ++ ;
}
 void CMUTMapChunk::RenderGrass( LPDIRECT3DDEVICE9 pDevice ){
	if ( !visible ) return;
	 grass.Render( pDevice );
 }
 void CMUTMapChunk::RenderTreeTrunk(){
	 if ( !tnum || !visible ) return;
	 treeSet.RenderTrunk();
 }
 void CMUTMapChunk::RenderWater(){
	 if ( !visible || !watered ) return;
	 water.Render( MUTState()->GetDevice() );
 }
 void CMUTMapChunk::RenderTreeLeaf(){
	 if ( !tnum || !visible ) return;
	 treeSet.RenderLeaf();
 }

////////////////////////////////////////////////////////////


CMUTMapTile::CMUTMapTile() : blend(0), bLoading(false), bEmpty(false) {
	coord.x = 0xfff;
	coord.y = 0xfff;
}

const BYTE MAPMARK_UP = 8;
const BYTE MAPMARK_RIGHT= 4;
const BYTE MAPMARK_LEFT = 2;
const BYTE MAPMARK_DOWN = 1;
HRESULT CMUTMapTile::Create( POINT c, HANDLE hHeightFile ){
	DWORD dwRead = 0;
	DWORD dwNeed = sizeof(float)*MAPTILE_HEIGHTMAP_SIZE*MAPTILE_HEIGHTMAP_SIZE;
	ReadFile( hHeightFile, fheightmap, dwNeed, &dwRead, NULL );
	/*
	if ( blend )
		blend->Release();*/
	static WCHAR blendtmp[64];
	wsprintf( blendtmp, L"maps/%d_%d.png", c.x, c.y );
	HANDLE hBlend = CreateFile( blendtmp, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
	if ( hBlend == INVALID_HANDLE_VALUE )
		lstrcpy( blendtmp, L"maps/default.png" );
	else
		CloseHandle( hBlend );

	if ( dwRead != dwNeed ){
		MUTErrorTrace( L"Read Heightmap failed" );
		return E_FAIL;
	}

	BYTE marks[16];
	int mcnt = 0;
	for ( int i = 0; i < MAPTILE_HEIGHTMAP_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_HEIGHTMAP_SIZE; j++ ){
			if ( !i && !j ){
				heightmap[i][j] = rand()%16;
			}else{
				mcnt = 0;
				for ( BYTE k = 0; k < 16; k++ ){
					if ( j-1>=0 ){
						 if ( (k & MAPMARK_LEFT) ^ (heightmap[i][j-1] & MAPMARK_RIGHT)>>1 )
							 continue;
					}
					if ( i-1>=0 ){
						 if ( (k & MAPMARK_DOWN) ^ (heightmap[i-1][j] & MAPMARK_UP)>>3 )
							 continue;
					}
					marks[ mcnt++ ] = k;
				}
				heightmap[i][j] = marks[ rand()%mcnt ];
			}
		}
	}

	WCHAR infotmp[64];
	wsprintf( infotmp, L"maps/%d_%d.info", c.x, c.y );

	MUTRequestBegin( hResEvent );
	coord = c;
	HANDLE hInfo = CreateFile( infotmp, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
	if ( hInfo == INVALID_HANDLE_VALUE ){
		MUTWinErrorTrace( GetLastError() );
		return E_FAIL;
	}
	DWORD read = 0;
	int num[2];
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[i][j].treed = false;
			chunks[i][j].grassed = true;
			if ( coord.x >= 0 )
				chunks[i][j].watered = true;
			ReadFile( hInfo, &num, sizeof(int)*2, &read, 0 );
			if ( FAILED( chunks[ i ][ j ].Create( blendtmp, hResEvent, num[0], num[1] ) ) ){
				return E_FAIL;
			}
		}
	}
	CloseHandle( hInfo );
	MUTRequestEnd( hResEvent );
	return S_OK;
}

float CMUTMapTile::GetHeight( int hvert, int wvert, float hLerp, float wLerp ){
	if ( bEmpty ) return 0;
	float h = 0;
	if ( hLerp >= wLerp ){
		if ( 1.0f - hLerp >= wLerp ){//left
		h = fheightmap[ hvert ][ wvert ];
		h = h + 2.0f * wLerp * ( fheightmap[ hvert+1 ][ wvert+1 ] - h );
		h = h + ( hLerp - wLerp ) * ( fheightmap[ hvert+2 ][ wvert ] - fheightmap[ hvert ][ wvert ] );
		}else{//up
		h = fheightmap[ hvert+2 ][ wvert ];
		h = h + 2.0f * ( 1 - hLerp ) * ( fheightmap[ hvert+1 ][ wvert+1 ] - h );
		h = h + ( wLerp - 1.0f + hLerp ) * ( fheightmap[ hvert+2 ][ wvert+2 ] - fheightmap[ hvert+2 ][ wvert ] );
		}
	}else{
		if ( hLerp >= 1.0f - wLerp ){//right
			h = fheightmap[ hvert ][ wvert+2 ];
			h = h + 2.0f * ( 1 - wLerp ) * ( fheightmap[ hvert+1 ][ wvert+1 ] - h );
			h = h + ( hLerp - 1.0f + wLerp ) * ( fheightmap[ hvert+2 ][ wvert+2 ] - fheightmap[ hvert ][ wvert+2 ] );
		}else{//bottom
			h = fheightmap[ hvert ][ wvert ];
			h = h + 2.0f * hLerp * ( fheightmap[ hvert+1 ][ wvert+1 ] - h );
			h = h + ( wLerp - hLerp ) * ( fheightmap[ hvert ][ wvert+2 ] - fheightmap[ hvert ][ wvert ] );
		}
	}
	return h;
}

D3DXVECTOR3 g_GrassPts[4096];
D3DXVECTOR3 g_TreePts[2];
void CMUTMapTile::InitVertexData( CMUTMapWorld* world ){
	MUTRequestBegin( hResEvent );
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[i][j].pIndexVoid = 0;
			chunks[i][j].pVertexVoid = 0;
			MUTRequestLock( hResEvent, MUTRES_VB, chunks[i][j].vb, 0, chunks[i][j].pVertexVoid );
			MUTRequestLock( hResEvent, MUTRES_IB, chunks[i][j].ib, 0, chunks[i][j].pIndexVoid );
			chunks[i][j].grass.vertsVoid = 0;
			chunks[i][j].grass.trisVoid = 0;
			if ( chunks[i][j].gnum ){
				MUTRequestLock( hResEvent, MUTRES_VB, chunks[i][j].grass.vb, 0, chunks[i][j].grass.vertsVoid );
				MUTRequestLock( hResEvent, MUTRES_IB, chunks[i][j].grass.ib, 0, chunks[i][j].grass.trisVoid );
			}
			chunks[i][j].treeSet.vertsVoid = 0;
			chunks[i][j].treeSet.trisVoid = 0;
			chunks[i][j].treeSet.lvertsVoid = 0;
			chunks[i][j].treeSet.ltrisVoid = 0;
			if ( chunks[i][j].tnum ){
				MUTRequestLock( hResEvent, MUTRES_VB, chunks[i][j].treeSet.vb, 0, chunks[i][j].treeSet.vertsVoid );
				MUTRequestLock( hResEvent, MUTRES_VB, chunks[i][j].treeSet.lvb, 0, chunks[i][j].treeSet.lvertsVoid );
				MUTRequestLock( hResEvent, MUTRES_IB, chunks[i][j].treeSet.ib, 0, chunks[i][j].treeSet.trisVoid );
				MUTRequestLock( hResEvent, MUTRES_IB, chunks[i][j].treeSet.lib, 0, chunks[i][j].treeSet.ltrisVoid );
			}

		}
	}
	MUTRequestEnd( hResEvent );

	WCHAR plantFileTmp[64];
	wsprintf( plantFileTmp, L"maps/%d_%d.plant", coord.x, coord.y );
	HANDLE hPlantFile = CreateFile( plantFileTmp, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );

	DWORD dwread = 0;

	D3DXVECTOR3 posBias( coord.x*MAPWORLD_TILEBIAS, 0, coord.y*MAPWORLD_TILEBIAS );
	D3DXVECTOR2 uvblendBias( 0, 0 );
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			POINT Bias = { j*(MAPCHUNK_HEIGHTMAP_SIZE-1), i*(MAPCHUNK_HEIGHTMAP_SIZE-1) };
			chunks[ i ][ j ].InitDefaultVertexData( world, uvblendBias + D3DXVECTOR2( j*1.0f/MAPTILE_SIZE, (MAPTILE_SIZE-1-i)*1.0f/MAPTILE_SIZE ),
				posBias + D3DXVECTOR3( j*MAPTILE_CHUNKBIAS, 0, i*MAPTILE_CHUNKBIAS ), heightmap, fheightmap, Bias );
			chunks[ i ][ j ].InitDefaultIndexData();
			
			if ( hPlantFile != INVALID_HANDLE_VALUE ){
				if ( chunks[i][j].gnum )
					ReadFile( hPlantFile, g_GrassPts, chunks[i][j].gnum*sizeof(D3DXVECTOR3), &dwread, 0 );
				if ( chunks[i][j].tnum )
					ReadFile( hPlantFile, g_TreePts, chunks[i][j].tnum*sizeof(D3DXVECTOR3), &dwread, 0 );
				if ( chunks[i][j].gnum )
					chunks[i][j].InitGrass( D3DXVECTOR3( coord.x*MAPWORLD_TILEBIAS, 0, coord.y*MAPWORLD_TILEBIAS ), g_GrassPts );
				if ( chunks[i][j].tnum )
					chunks[i][j].treeSet.InitData( D3DXVECTOR3( coord.x*MAPWORLD_TILEBIAS, 0, coord.y*MAPWORLD_TILEBIAS ), g_TreePts );
			}
		}
	}

	CloseHandle( hPlantFile );

	MUTRequestBegin( hResEvent );
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[i][j].pIndexVoid = 0;
			chunks[i][j].pVertexVoid = 0;
			MUTRequestUnlock( hResEvent, MUTRES_VB, chunks[i][j].vb );
			MUTRequestUnlock( hResEvent, MUTRES_IB, chunks[i][j].ib );
			chunks[i][j].grass.vertsVoid = 0;
			chunks[i][j].grass.trisVoid = 0;
			if ( chunks[i][j].gnum ){
				MUTRequestUnlock( hResEvent, MUTRES_VB, chunks[i][j].grass.vb );
				MUTRequestUnlock( hResEvent, MUTRES_IB, chunks[i][j].grass.ib );
			}
			chunks[i][j].treeSet.vertsVoid = 0;
			chunks[i][j].treeSet.trisVoid = 0;
			chunks[i][j].treeSet.lvertsVoid = 0;
			chunks[i][j].treeSet.ltrisVoid = 0;
			if ( chunks[i][j].tnum ){
				MUTRequestUnlock( hResEvent, MUTRES_VB, chunks[i][j].treeSet.vb );
				MUTRequestUnlock( hResEvent, MUTRES_VB, chunks[i][j].treeSet.lvb );
				MUTRequestUnlock( hResEvent, MUTRES_IB, chunks[i][j].treeSet.ib );
				MUTRequestUnlock( hResEvent, MUTRES_IB, chunks[i][j].treeSet.lib );
			}
		}
	}
	MUTRequestEnd( hResEvent );

	/*/blend selfshadow
	static bool test = false;
	if (test) return;
	test=true;
	blend = g_mapTexManager.GetTexture( L"maps/blend.png" );
	LPDIRECT3DTEXTURE9 pTex = blend->pTex;
	D3DLOCKED_RECT locked;
	D3DSURFACE_DESC desc;
	pTex->LockRect( 0, &locked, NULL, 0 );
	pTex->GetLevelDesc( 0, &desc );
	int mapSize = ( MAPTILE_HEIGHTMAP_SIZE - 1 ) / 2;
	int step = desc.Height / mapSize;
	for ( int i = 0; i < desc.Height; i++ ){
		for ( int j = 0; j < desc.Width; j++ ){
			 int hvert = 2 * ( mapSize - 1 - i / step );
			 float hLerp = 1.0f - i % step / float( step );
			 int wvert = 2 * ( j / step );
			 float wLerp = j % step / float( step );
			 float h = GetHeight( hvert, wvert, hLerp, wLerp );
			 D3DXVECTOR3 ldir(0,MAPCHUNK_SIZE/step/2.0f,1.0f/step);
			 int k = 0; 
			 float shadow = 1;
			 while ( k < 128 ){
				hLerp += ldir.z;
				wLerp += ldir.x;
				hvert += 2 * int( hLerp );
				wvert += 2 * int( wLerp );
				hLerp -= int( hLerp );
				wLerp -= int( wLerp );
				if ( hvert < 0 || hvert >= MAPTILE_HEIGHTMAP_SIZE-3 || wvert < 0 || wvert >= MAPTILE_HEIGHTMAP_SIZE-3 )
					break;
				float curh = GetHeight( hvert, wvert, hLerp, wLerp );
				h+=ldir.y;
				if ( curh > h ){
					shadow = (k+36)/100.0f;
					break;
				}
				k++;
			 }
			 LPDWORD color = LPDWORD( LPBYTE( locked.pBits ) + i * locked.Pitch ) + j;
			 D3DXCOLOR c = *color;
			 c.a = shadow;
			 *color = c;
		}
	}
	pTex->UnlockRect( 0 );

	D3DXSaveTextureToFile( L"maps/blendshadow.png", D3DXIFF_PNG, pTex, NULL );*/
}
const int anum = 15;
const int hanum = anum/2;
static float a[2][anum] =
{
{ 0.98877,0.957031,0.907715,0.84375,0.768066,0.683594,0.593262,0.5,0.406738,0.316406,0.231934,0.15625,0.0922852,0.0429688,0.0112305},
{ 0.0112305,0.0429688,0.0922852,0.15625,0.231934,0.316406,0.406738,0.5,0.593262,0.683594,0.768066,0.84375,0.907715,0.957031,0.98877},
};
float nh[64];
void CMUTMapTile::FixLeftEdge( CMUTMapTile* pLeftTile){
	if ( pLeftTile ){
		for( int t = 0; t < MAPTILE_HEIGHTMAP_SIZE; t++ ){
			for( int j = 0; j < anum; j++ ){
				nh[j] = a[0][j]*pLeftTile->fheightmap[ t ][ MAPTILE_HEIGHTMAP_SIZE-hanum-2 ] + 
						a[1][j]*fheightmap[ t ][ hanum ];
			}
			for( int j = 0; j<=hanum; j++){
				pLeftTile->fheightmap[ t ][ MAPTILE_HEIGHTMAP_SIZE-1-hanum+j ] = nh[j];
			}
			for( int j = hanum; j<anum; j++){
				fheightmap[ t ][ j-hanum ] = nh[j];
			}
		}
	}
}

void CMUTMapTile::FixUpEdge( CMUTMapTile* pUpTile ){
	if ( pUpTile ){
		for( int t = 0; t < MAPTILE_HEIGHTMAP_SIZE; t++ ){
			for( int j = 0; j < anum; j++ ){
				nh[j] = a[0][j]*pUpTile->fheightmap[ hanum ][ t ] + 
						a[1][j]*fheightmap[ MAPTILE_HEIGHTMAP_SIZE-hanum-2 ][ t ];
			}
			for( int j = 0; j<=hanum; j++){
				pUpTile->fheightmap[ hanum-j ][ t ] = nh[j];
			}
			for( int j = hanum; j<anum; j++){
				fheightmap[ MAPTILE_HEIGHTMAP_SIZE-1-j+hanum ][ t ] = nh[j];
			}
		}
	}
}

void CMUTMapTile::Culling( D3DXPLANE* p ){
	if ( bEmpty ) return;
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[ i ][ j ].Culling( p );
		}
	}
}

void CMUTMapTile::Render( LPDIRECT3DDEVICE9 pDevice ){
	if ( bLoading||bEmpty ) return;
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[ i ][ j ].Render( pDevice );
		}
	}
}

void CMUTMapTile::RenderGrass( LPDIRECT3DDEVICE9 pDevice ){
	if ( bLoading||bEmpty ) return;
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[ i ][ j ].RenderGrass( pDevice );
		}
	}
}

void CMUTMapTile::RenderTreeTrunk(){
	if ( bLoading||bEmpty ) return;
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[ i ][ j ].RenderTreeTrunk();
		}
	}
}

void CMUTMapTile::RenderTreeLeaf(){
	if ( bLoading||bEmpty ) return;
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[ i ][ j ].RenderTreeLeaf();
		}
	}
}

void CMUTMapTile::RenderWater(){
	return;
	if ( bLoading||bEmpty ) return;
	for ( int i = 0; i < MAPTILE_SIZE; i++ ){
		for ( int j = 0; j < MAPTILE_SIZE; j++ ){
			chunks[ i ][ j ].RenderWater();
		}
	}
}

void CMUTMapTile::Release(){
	for ( int i = 0; i < MAPTILE_SIZE; i++ )
		for ( int j = 0; j < MAPTILE_SIZE; j++ )
			chunks[i][j].Release();
	coord.x = 0xfff;
	coord.y = 0xfff;
	if ( blend )
		blend->Release();
	blend = 0;
}

///////////////////////////////////////////////////////


CMUTMapWorld::CMUTMapWorld(){
	ZeroMemory( usedCache, sizeof( usedCache ) );
	ZeroMemory( tileShow, sizeof( tileShow ) );
	pDepthTex = 0;
}

HRESULT CMUTMapWorld::LoadMapTile( POINT coord, CMUTMapTile& tile ){
	HANDLE hHeightFile;
	WCHAR tmp[32];
	wsprintf( tmp, L"maps/%d_%d.fraw", coord.x, coord.y );
	hHeightFile = CreateFile( tmp, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( INVALID_HANDLE_VALUE == hHeightFile )
		hHeightFile = CreateFile( L"maps/default.fraw", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( INVALID_HANDLE_VALUE == hHeightFile ){
		MUTErrorTrace( L"Invalid Height file" );
		return E_FAIL;
	}
	tile.Create( coord, hHeightFile );
	tile.InitVertexData( this );
	//tile.coord = coord;
	//D3DXVECTOR3( coord.x*MAPWORLD_TILEBIAS, 0, coord.y*MAPWORLD_TILEBIAS ),
	CloseHandle( hHeightFile );
	return S_OK;
}

void CMUTMapWorld::Culling( D3DXMATRIX m ){
	D3DXPLANE plane[6];
	// plane normals are pointing to the inside halfspace
	// left
	plane[0].a = m._14 + m._11;
	plane[0].b = m._24 + m._21;
	plane[0].c = m._34 + m._31;
	plane[0].d = m._44 + m._41;
	// right
	plane[1].a = m._14 - m._11;
	plane[1].b = m._24 - m._21;
	plane[1].c = m._34 - m._31;
	plane[1].d = m._44 - m._41;
	// bottom
	plane[2].a = m._14 + m._12;
	plane[2].b = m._24 + m._22;
	plane[2].c = m._34 + m._32;
	plane[2].d = m._44 + m._42;
	// top
	plane[3].a = m._14 - m._12;
	plane[3].b = m._24 - m._22;
	plane[3].c = m._34 - m._32;
	plane[3].d = m._44 - m._42;
	// near
	plane[4].a = m._13;
	plane[4].b = m._23;
	plane[4].c = m._33;
	plane[4].d = m._43;
	// far
	plane[5].a = m._14 - m._13;
	plane[5].b = m._24 - m._23;
	plane[5].c = m._34 - m._33;
	plane[5].d = m._44 - m._43;
	for ( int  i = 0; i < 6; i++ )
		D3DXPlaneNormalize( plane + i, plane + i );
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] )
				tileShow[i][j]->Culling( plane );
		}
	}
}
extern CMUTCharacter g_md5;
/*
const int treenum = 10;
CMUTTreeTrunk trunk[treenum];*/
CMUTTexture* trunktex = 0, *leaftex = 0, *leafalpha = 0;
void CMUTMapWorld::Render( CMUTCamera& c ){
	g_chunksdrawn = 0;
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( !pDevice ) return;
	
	static bool test = false;
	if ( !test ){
		trunktex = g_mapTexManager.GetTexture( L"tree/trunk.jpg" );
		leaftex = g_mapTexManager.GetTexture( L"tree/leaf_diffuse.jpg" );
		leafalpha = g_mapTexManager.GetTexture( L"tree/leaf_alpha.jpg" );
		test = true;
	}
	g_md5.UpdateFrame();
	
	D3DXMATRIX matLightOrtho, matLightOrthoSmall, matLightView;
	D3DXVECTOR3 lightPos = c.pos + D3DXVECTOR3( 0, 1, 1 ) * 5000.0f;
	D3DXMatrixLookAtLH( &matLightView, &lightPos, &c.pos, &D3DXVECTOR3( 0, 1, 0 ) );	
	D3DXMatrixOrthoLH( &matLightOrtho, 3000.0f, 3000.0f, 1.0f, 20000.0f );
	D3DXMatrixOrthoLH( &matLightOrthoSmall, 20000.0f, 20000.0f, 1.0f, 20000.0f );
	RenderDepth( c, matLightView, matLightOrtho, matLightOrthoSmall );

	pDevice->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 0, 0, 0 ), 1.0f, 0 );
	D3DXMATRIX matWorld, matView, matProjection;
	D3DXMatrixIdentity( &matWorld );

	pDevice->SetTransform( D3DTS_WORLD, &matWorld );

	sky.Render();
	sky.RenderSun();
	sky.RenderCloud();

	//UpdateTiles( c );
	
	float fogStart = 4000.0f, fogEnd = 40000.0f;
	pDevice->SetRenderState( D3DRS_FOGENABLE, TRUE );
	pDevice->SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_LINEAR );
	pDevice->SetRenderState( D3DRS_FOGSTART, *(DWORD*)&fogStart );
	pDevice->SetRenderState( D3DRS_FOGEND, *(DWORD*)&fogEnd );
	D3DXCOLOR fogColor;
	D3DXColorLerp( &fogColor, &D3DXCOLOR(1,1,1,1), &D3DXCOLOR( 60.0f/256, 120.0f/256, 240.0f/256, 1 ), 0.3 );
	pDevice->SetRenderState( D3DRS_FOGCOLOR, fogColor );

	pDevice->GetTransform( D3DTS_VIEW, &matView );
	pDevice->GetTransform( D3DTS_PROJECTION, &matProjection );

	Culling( matView*matProjection );
	
	for ( int i = 0 ; i < 5; i++ ){
		pDevice->SetSamplerState( i, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
		pDevice->SetSamplerState( i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
		pDevice->SetSamplerState( i, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	}
	for ( int i = 0; i < 5; i++ )
		pDevice->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, 0 );
	pDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 1 );
	pDevice->SetTextureStageState( 2, D3DTSS_TEXCOORDINDEX, 2 );


	UINT Passes = 0;
	g_mapEffect->SetTechnique( "techTerrain" );
	g_mapEffect->SetMatrix( "g_matWorld", &matWorld );
	g_mapEffect->SetMatrix( "g_matView", &matView );
	g_mapEffect->SetMatrix( "g_matProjection", &matProjection );
	g_mapEffect->SetMatrix( "g_matLightViewProj", &( matLightView*matLightOrtho ) );
	g_mapEffect->SetMatrix( "g_matLightViewProjSmall", &( matLightView*matLightOrthoSmall ) );
	g_mapEffect->SetVector( "g_cameraPos", &D3DXVECTOR4( c.pos.x, c.pos.y, c.pos.z, 1.0f ) );

	
	pDevice->SetTexture( 5, pDepthTex );
	pDevice->SetSamplerState( 5, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER );
	pDevice->SetSamplerState( 5, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER );
	pDevice->SetSamplerState( 5, D3DSAMP_BORDERCOLOR, D3DCOLOR_XRGB( 255, 255, 255 ) );
	pDevice->SetSamplerState( 5, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 5, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	pDevice->SetTexture( 6, pDepthTexSmall );
	pDevice->SetSamplerState( 6, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER );
	pDevice->SetSamplerState( 6, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER );
	pDevice->SetSamplerState( 6, D3DSAMP_BORDERCOLOR, D3DCOLOR_XRGB( 255, 255, 255 ) );
	pDevice->SetSamplerState( 6, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 6, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );

	g_mapEffect->Begin( &Passes, 0 );
	g_mapEffect->BeginPass( 0 );
	
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] )
				tileShow[i][j]->Render( pDevice );
		}
	}

	g_mapEffect->EndPass();
	g_mapEffect->End();
	
	
	
	static float grassTime = 0;
	grassTime += MUTState()->GetElapsedTime();
	g_grassEffect->SetMatrix( "g_matWorld", &matWorld );
	g_grassEffect->SetMatrix( "g_matView", &matView );
	g_grassEffect->SetMatrix( "g_matProjection", &matProjection );
	g_grassEffect->SetMatrix( "g_matLightViewProj", &( matLightView*matLightOrtho ) );
	g_grassEffect->SetMatrix( "g_matLightViewProjSmall", &( matLightView*matLightOrthoSmall ) );
	g_grassEffect->SetFloat( "g_time", grassTime );
	
	g_grassEffect->SetTechnique( "techTreeTrunk" );
	g_grassEffect->Begin( &Passes, 0 );
	pDevice->SetTexture( 0, *trunktex );
	pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	g_grassEffect->BeginPass( 0 );
	
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] )
			tileShow[i][j]->RenderTreeTrunk();
		}
	}
	g_grassEffect->EndPass();
	g_grassEffect->End();
	
	g_grassEffect->SetTechnique( "techGrass" );
	g_grassEffect->Begin( &Passes, 0 );
	g_grassEffect->SetBool( "g_bShadowed", TRUE );
	g_grassEffect->BeginPass( 0 );
	
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] )
			tileShow[i][j]->RenderGrass( pDevice );
		}
	}

	g_grassEffect->EndPass();
	pDevice->SetTexture( 0, *leafalpha );
	pDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	pDevice->SetTexture( 1, *leaftex );
	pDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	

	g_grassEffect->BeginPass( 1 );
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] )
			tileShow[i][j]->RenderTreeLeaf();
		}
	}
	g_grassEffect->EndPass();
	g_grassEffect->End();

	D3DXVECTOR3 md5Pos(c.pos);
	g_md5.pos = D3DXVECTOR3(md5Pos.x, GetHeight( md5Pos.x, md5Pos.z ), md5Pos.z);
	
	D3DXMATRIX matDir;
	D3DXVECTOR3 lookDir( 0, 0, 1 ), rightDir( 1, 0, 0 );
	lookDir.y = 0; rightDir.y = 0;
	D3DXVec3Normalize( &lookDir, &lookDir );
	D3DXVec3Normalize( &rightDir, &rightDir );
	matDir._11 = lookDir.x; matDir._12 = 0; matDir._13 = lookDir.z; matDir._14 = 0;
	matDir._21 = rightDir.x; matDir._22 = 0; matDir._23 = rightDir.z; matDir._24 = 0;
	matDir._31 = 0; matDir._32 = 1; matDir._33 = 0; matDir._34 = 0;
	matDir._41 = 0; matDir._42 = 0; matDir._43 = 0; matDir._44 = 1;
	//D3DXMatrixTranspose( &matDir, &matDir );
	//pDevice->SetTransform( D3DTS_WORLD, &(matDir*matWorld) );
	//pDevice->SetTransform( D3DTS_VIEW, &c.GetMatrix() );
	static int animcnt = 0;
	static bool added = true;
	if ( GetKeyState('T') & 0xff00 ){
		if ( added ){
			g_md5.SetAnim( animcnt );
			animcnt++;
		}
		added = false;
	}else{
		added = true;
	}

	g_mapEffect->SetTechnique( "techMD5Model" );
	g_mapEffect->Begin( &Passes, 0 );
	g_mapEffect->BeginPass( 0 );
	g_md5.Render( g_mapEffect );
	g_mapEffect->EndPass();
	g_mapEffect->End();

	D3DXMatrixIdentity( &matWorld );
	pDevice->SetTransform( D3DTS_WORLD, &matWorld );

	LPDIRECT3DSURFACE9 pTargetSurface = 0, pScreenSurface = 0;
	pDevice->GetRenderTarget( 0, &pTargetSurface );
	pScreenTex->GetSurfaceLevel( 0, &pScreenSurface );
	pDevice->StretchRect( pTargetSurface, 0, pScreenSurface, 0, D3DTEXF_NONE );
	pTargetSurface->Release();
	pScreenSurface->Release();
	//water

	static CMUTTexture* waterheight = 0;
	static CMUTTexture* waterfoam = 0;
	static bool normaltest = false;
	if ( !normaltest ){
		waterheight = g_mapTexManager.GetTexture( L"waterdis.jpg" );
		waterfoam = g_mapTexManager.GetTexture( L"foam.jpg" );
		normaltest = true;
	}/*
	static float watertime = 0;
	watertime += MUTState()->GetElapsedTime();
	g_mapEffect->SetFloat( "g_time", watertime );
	pDevice->SetTexture( 0, pDepthTex );
	pDevice->SetTexture( 1, pScreenTex );
	pDevice->SetTexture( 2, *waterheight );
	pDevice->SetTexture( 3, *waterfoam );
	pDevice->SetSamplerState( 2, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	g_mapEffect->SetTechnique( "techWater" );
	g_mapEffect->Begin( &Passes, 0 );
	g_mapEffect->BeginPass( 0 );
	
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] )
			tileShow[i][j]->RenderWater();
		}
	}
	g_mapEffect->EndPass();
	g_mapEffect->End();
	*/
	pDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
	
	/*/Blend sky
	g_mapEffect->SetTechnique( "techSkyBlend" );
	g_mapEffect->Begin( &Passes, 0 );
	D3DXMATRIX matViewNoTrans;
	pDevice->GetTransform( D3DTS_VIEW, &matViewNoTrans );
	matViewNoTrans._41 = matViewNoTrans._42 = matViewNoTrans._43 = 0;
	g_mapEffect->SetMatrix( "g_matView", &matViewNoTrans );
	pDevice->SetTexture( 0, sky.skytex );
	pDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	pDevice->SetTexture( 1, pDepthTex );
	g_mapEffect->BeginPass( 0 );
	//sky.Render();
	g_mapEffect->EndPass();
	g_mapEffect->End();*/

	sky.QuerySun();
	sky.RenderLensFlare();
}



struct SSAOVERTEX{
	D3DXVECTOR3 pos;
	D3DXVECTOR2 uv;
};

void CMUTMapWorld::RenderDepth( CMUTCamera& c, D3DXMATRIX matLightView, D3DXMATRIX matLightOrtho, D3DXMATRIX matLightOrthoSmall ){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( !pDevice ) return;
	D3DXMATRIX matWorld;

	Culling( matLightView*matLightOrtho );
	// Render Ortho Depth
	LPDIRECT3DSURFACE9 pDepthTarget = 0, pDepthTargetSmall = 0, pOldTarget = 0, pOldDepthStencil = 0;
	pDevice->GetDepthStencilSurface( &pOldDepthStencil );
	pDevice->GetRenderTarget( 0, &pOldTarget );

	pDepthTex->GetSurfaceLevel( 0, &pDepthTarget );
	pDevice->SetDepthStencilSurface( pDepthStencil );
	pDevice->SetRenderTarget( 0, pDepthTarget );

	pDevice->Clear( 0, 0, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,255,255), 1.0f, 0 );

	D3DXMatrixIdentity( &matWorld );	

	g_mapEffect->SetMatrix( "g_matWorld", &matWorld );
	g_mapEffect->SetMatrix( "g_matView", &matLightView );
	g_mapEffect->SetMatrix( "g_matProjection", &matLightOrtho );

	g_mapEffect->SetTechnique( "techOrthoDepth" );
	UINT Passes = 0;
	g_mapEffect->Begin( &Passes, 0 );
	g_mapEffect->BeginPass( 0 );
	
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] )
				tileShow[i][j]->RenderTreeTrunk();
		}
	}

	g_mapEffect->EndPass();

	// leaf with alphatest
	g_mapEffect->BeginPass( 1 );

	pDevice->SetTexture( 0, *leafalpha );
	
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] )
				tileShow[i][j]->RenderTreeLeaf();
		}
	}

	g_mapEffect->EndPass();

	g_mapEffect->End();

	//Small-sized depth tex
	pDepthTexSmall->GetSurfaceLevel( 0, &pDepthTargetSmall );
	pDevice->SetRenderTarget( 0, pDepthTargetSmall );
	pDevice->Clear( 0, 0, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,255,255), 1.0f, 0 );
	
	Culling( matLightView*matLightOrthoSmall );
	
	g_mapEffect->SetMatrix( "g_matProjection", &matLightOrthoSmall );
	g_mapEffect->Begin( &Passes, 0 );
	g_mapEffect->BeginPass( 0 );
	
	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] ){
				tileShow[i][j]->RenderTreeTrunk();
			}
		}
	}

	g_mapEffect->EndPass();

	// leaf with alphatest
	g_mapEffect->BeginPass( 1 );

	pDevice->SetTexture( 0, *leafalpha );

	for ( int i = 0; i < 3; i++ ){
		for ( int j = 0; j < 3; j++ ){
			if ( tileShow[i][j] ){
				tileShow[i][j]->RenderTreeLeaf();
			}
		}
	}

	g_mapEffect->EndPass();

	g_mapEffect->End();

	
	pDevice->SetRenderTarget( 0, pOldTarget );
	pDevice->SetDepthStencilSurface( pOldDepthStencil );
	pOldTarget->Release();
	pOldDepthStencil->Release();
	pDepthTarget->Release();
	pDepthTargetSmall->Release();

	if ( GetKeyState('P') & 0xff00 ){
		D3DXSaveTextureToFile( L"depth.jpg", D3DXIFF_JPG, pDepthTex, 0 );
		D3DXSaveTextureToFile( L"depthsmall.jpg", D3DXIFF_JPG, pDepthTexSmall, 0 );
	}
}

DWORD WINAPI ThreadLoadMapTile( LPVOID lpParam ){
	CMUTMapTile* tile = ( CMUTMapTile * )lpParam;

	HANDLE hHeightFile;
	WCHAR tmp[32];
	wsprintf( tmp, L"maps/%d_%d.fraw", tile->coord.x, tile->coord.y );
	hHeightFile = CreateFile( tmp, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( INVALID_HANDLE_VALUE == hHeightFile ){
		tile->bLoading = false;
		tile->bEmpty = true;
		return 0;
	}
	if ( INVALID_HANDLE_VALUE == hHeightFile ){
		MUTErrorTrace( L"Invalid Height file" );
		return E_FAIL;
	}

	WaitForSingleObject( *( tile->threadParam.pSingleTileEvent ), INFINITE );

	tile->Create( tile->coord, hHeightFile );
	tile->InitVertexData( tile->threadParam.world );

	// Other threads can run now
	SetEvent( *( tile->threadParam.pSingleTileEvent ) );

	tile->bEmpty = false;
	tile->bLoading = false;
	CloseHandle( hHeightFile );
	return S_OK;
}

void CMUTMapWorld::UpdateTiles( CMUTCamera& c ){
	ZeroMemory( tileShow, sizeof( tileShow ) );

	POINT coord;
	coord.x = LONG( c.pos.x / MAPWORLD_TILEBIAS );
	coord.y = LONG( c.pos.z / MAPWORLD_TILEBIAS );
	if ( c.pos.x < 0 )
		coord.x--;
	if ( c.pos.z < 0 )
		coord.y--;

	POINT scoord;
	float subsize = MAPWORLD_TILEBIAS*0.5f;
	scoord.x = LONG( ( c.pos.x - MAPWORLD_TILEBIAS*0.25f ) / subsize );
	scoord.y = LONG( ( c.pos.z - MAPWORLD_TILEBIAS*0.25f ) / subsize );

	for ( int i = -3; i <= 3; i++ ){
		for ( int j = -3; j <=3; j++ ){
			int cx = ( scoord.x + j ) / 2;
			int cy = ( scoord.y + i ) / 2;
			bool found = false;
			int k = 0;
			for ( ; k < MAPWORLD_CACHE; k++ ){
				if ( ( tileCache[k].coord.x == cx && tileCache[k].coord.y == cy ) || 
					 ( tileCache[k].bLoading && tileCache[k].coord.x == cx && tileCache[k].coord.y == cy ) ){
					found = true;
					break;
				}
			}
			if ( found ) continue;
			for ( k = 0; k < MAPWORLD_CACHE; k++ ){
				if ( tileCache[k].bLoading )
					continue;
				if ( abs( 2*tileCache[k].coord.x - scoord.x ) > 3 || abs( 2*tileCache[k].coord.y - scoord.y ) > 3 )
					break;
			}
			if ( k >= MAPWORLD_CACHE )
				continue;
			tileCache[k].bLoading = true;
			tileCache[k].coord.x = cx;
			tileCache[k].coord.y = cy;
			tileCache[k].threadParam.pSingleTileEvent = &hSingleTileEvent;
			DWORD threadID;
			HANDLE hThread = CreateThread( NULL, 0, ThreadLoadMapTile, &tileCache[k], 0, &threadID );
			CloseHandle( hThread );
			//ThreadLoadMapTile( &tileCache[k] );
		}
	}

	int cnt = 0;
	for ( int i = 0; i < MAPWORLD_CACHE; i++ ){
		if ( tileCache[i].bLoading )
			continue;
		POINT& tc = tileCache[ i ].coord;
		LONG dx = tc.x - coord.x;
		LONG dy = tc.y - coord.y;
		if ( abs( dx ) <= 1 && abs( dy ) <= 1 ){
			tileShow[ 1+dy ][ 1+dx ] = &tileCache[ i ];
		}
	}
	/*
	if ( cnt < 9 ){
		for ( int i = 0; i < 3 ; i++ ){
			for ( int j = 0; j <3 ; j++ ){
				if ( !tileShow[ i ][ j ] ){
					CMUTMapTile* unusedTile = NULL;
					int k = 0;
					for ( ; k < MAPWORLD_CACHE; k++ ){
						if ( !usedCache[ k ] ){
							unusedTile = &tileCache[ k ];
							break;
						}
					}
					if ( !unusedTile )
						MUTErrorTrace( L"Can't find unused tile" );
					POINT c = { j-1+coord.x , i-1+coord.y };
					tileShow[ i ][ j ] = unusedTile;
					usedCache[ k ] = 1;
					LoadMapTile( c, *unusedTile );
				}
			}
		}
		//FixTileEdge();
	}*/
}

void CMUTMapWorld::FixTileEdge(){/*
	for ( int i = 0; i < 3; i++ )
		for ( int j = 1; j < 3; j++ )
			tileShow[ i ][ j ]->FixLeftEdge( tileShow[ i ][ j-1 ] );
	for ( int i = 0; i < 2; i++ )
		for ( int j = 0; j < 3; j++ )
			tileShow[ i ][ j ]->FixUpEdge( tileShow[ i+1 ][ j ] );
	
	for ( int i = 0; i < 3; i++ )
		for ( int j = 0; j < 3; j++ )
			tileShow[ i ][ j ]->InitVertexData( this );*/
}

float CMUTMapWorld::GetHeight( float x, float z ){
	if ( !tileShow[0][0] ) return 0;
	x -= tileShow[0][0]->coord.x*MAPWORLD_TILEBIAS;
	z -= tileShow[0][0]->coord.y*MAPWORLD_TILEBIAS;
	int xtile = x / MAPWORLD_TILEBIAS;
	int ztile = z / MAPWORLD_TILEBIAS;
	if ( xtile > 2 || ztile > 2 || xtile < 0 || ztile < 0 ){
		//MUTErrorTrace( L"world getheight out of range" );
		return 0;
	}
	if ( tileShow[ztile][xtile] ){
		x -= xtile*MAPWORLD_TILEBIAS;
		z -= ztile*MAPWORLD_TILEBIAS;
		int hvert = z / MAPCHUNK_SIZE;
		int wvert = x / MAPCHUNK_SIZE;
		float hLerp = ( z - hvert*MAPCHUNK_SIZE )/MAPCHUNK_SIZE;
		float wLerp = ( x - wvert*MAPCHUNK_SIZE )/MAPCHUNK_SIZE;
		return tileShow[ztile][xtile]->GetHeight( 2*hvert, 2*wvert, hLerp, wLerp );
	}
	return 0.0f;
}

void CMUTMapWorld::Release(){
	for ( int i = 0; i < MAPWORLD_CACHE; i++ ){
		tileCache[i].Release();
	}
	ZeroMemory( tileShow, sizeof( tileShow ) );
	ZeroMemory( usedCache, sizeof( usedCache ) );
	if ( pDepthTex )
		pDepthTex->Release();
	pDepthTex = 0;
	if ( pScreenTex )
		pScreenTex->Release();
	pScreenTex = 0;
	sky.Release();
}

char g_mapEffectData[] = "\
sampler t0 : register(s0);\
sampler t1 : register(s1);\
sampler t2 : register(s2);\
sampler t3 : register(s3);\
sampler blend : register(s4);\
sampler sm : register(s5);\
sampler sm2 : register(s6);\
matrix g_matWorld; \
matrix g_matView; \
matrix g_matProjection; \
matrix g_matLightViewProj;\
matrix g_matLightViewProjSmall;\
float4 g_cameraPos;\
struct VS_OUTPUT{\
	float4 vPosition : POSITION0;\
	float2 vTexCoord : TEXCOORD0;\
	float2 vBlendCoord : TEXCOORD1;\
	float FogFactor : TEXCOORD2;\
	float3 vNormal : TEXCOORD3;\
	float3 vWorldPos : TEXCOORD4;\
};\
VS_OUTPUT VSTerrain( float3 vInPosition : POSITION0, float3 vInNormal : NORMAL0, float2 vInTexCoord : TEXCOORD0, float2 vInBlendCoord : TEXCOORD1 ){\
	VS_OUTPUT Out = (VS_OUTPUT)0;\
	Out.vWorldPos = mul( float4( vInPosition, 1.0f ), g_matWorld );\
	Out.vPosition = mul( mul( float4( Out.vWorldPos, 1.0f ), g_matView ), g_matProjection );\
	Out.vTexCoord = vInTexCoord;\
	Out.vBlendCoord = vInBlendCoord;\
	Out.FogFactor = saturate( ( length( Out.vWorldPos - g_cameraPos.xyz ) - 15000.0f ) / 20000.0f );\
	Out.vNormal = vInNormal;\
	return Out;\
}\
float2 shadowOffset = float2( 1.0f / 1024.0f, 1.0f / 1024.0f );\
float4 lightOrthoCoord( float3 vWorldPos, matrix matLightViewProj ){\
	float4 orthoPos = mul( float4( vWorldPos, 1.0f ), matLightViewProj );\
	orthoPos /= orthoPos.w;\
	orthoPos.x = ( orthoPos.x + 1.0f ) * 0.5f;\
	orthoPos.y = ( 1.0f - orthoPos.y ) * 0.5f;\
	return orthoPos;\
}\
float shadowPCF4x4( sampler s, float2 offset, float4 orthoPos ){\
	float x, y, shadow = 0;\
	for ( y = -1.5f; y <= 1.5f; y += 1.0f ){\
		for ( x = -1.5f; x <= 1.5f; x += 1.0f ){\
			shadow += tex2D( s, orthoPos.xy + offset * float2( x, y ) ).r;\
		}\
	}\
	return shadow * 0.0625f;\
}\
float shadowPCF( sampler s, float4 orthoPos ){\
	return tex2Dproj( s, float4( orthoPos.xy, orthoPos.z, 1.0f ) ).r;\
}\
float4 PSTerrain( float2 tileCoord : TEXCOORD0, float2 blendCoord : TEXCOORD1, float FogFactor : TEXCOORD2, float3 vNormal : TEXCOORD3, float3 vWorldPos : TEXCOORD4 ) : COLOR0{	\
	float4 c0 = tex2D( t0, tileCoord );\
	float4 c1 = tex2D( t1, tileCoord );\
	float4 c2 = tex2D( t2, tileCoord );\
	float4 c3 = tex2D( t3, tileCoord );\
	float4 blend = tex2D( blend, blendCoord );\
	float4 orthoPos = lightOrthoCoord( vWorldPos, g_matLightViewProj );\
	float shadow = 1.0f;\
	if ( orthoPos.x >= 1.0f || orthoPos.x <= 0.0f || orthoPos.y >= 1.0f || orthoPos.y <= 0.0f )\
		shadow = shadowPCF( sm2, lightOrthoCoord( vWorldPos, g_matLightViewProjSmall ) );\
	else\
		shadow = shadowPCF4x4( sm, shadowOffset, orthoPos );\
	float4 retColor = lerp( lerp( lerp( c0, c1, blend.r ) * exp( -1.5f * saturate( -blend.r * ( blend.r - 1.0f ) ) ), c2, blend.g ) * exp( -1.5f * saturate( -blend.g * ( blend.g - 1.0f ) ) ), c3, blend.b );\
	retColor.a = saturate( FogFactor + ( blend.a - 0.5f )/5.0f );\
	retColor.rgb *= blend.a*shadow;\
	return retColor;\
}\
struct VS_ORTHODEPTH{\
	float4 vPosition : POSITION0;\
	float2 vTexCoord : TEXCOORD0;\
	float zDepth : TEXCOORD1;\
};\
VS_ORTHODEPTH VSOrthoDepth( float3 vInPosition : POSITION0, float2 vTexCoord : TEXCOORD0 ){\
	VS_ORTHODEPTH Out = (VS_ORTHODEPTH)0;\
	float4 vWorldPos = mul( float4( vInPosition, 1.0f ), g_matWorld );\
	float4 vViewPos = mul( vWorldPos, g_matView );\
	Out.vTexCoord = vTexCoord;\
	Out.vPosition = mul( vViewPos, g_matProjection );\
	Out.zDepth = Out.vPosition.z/Out.vPosition.w+0.001f;\
	return Out;\
}\
float4 PSOrthoDepth( float zDepth : TEXCOORD1 ):COLOR0{\
	return float4( zDepth, zDepth, zDepth, zDepth );\
}\
float4 PSOrthoDepthGrass( float2 vTexCoord : TEXCOORD0, float zDepth : TEXCOORD1 ) : COLOR0{\
	float a = tex2D( t0, vTexCoord ).r;\
	clip( a - 0.2734375f );\
	return float4( zDepth, zDepth, zDepth, zDepth );\
}\
struct WATEROUTPUT{\
	float4 vPosition : POSITION0;\
	float2 vTexCoord : TEXCOORD0;\
	float3 vPos : TEXCOORD1;\
};\
WATEROUTPUT VSWater( float3 vInPosition : POSITION0, float2 vTexCoord : TEXCOORD0 ){\
	WATEROUTPUT Out = ( WATEROUTPUT )0;\
	Out.vPos = mul( float4( vInPosition, 1 ), g_matWorld ).xyz;\
	Out.vPosition = mul( mul( float4( Out.vPos, 1 ), g_matView ), g_matProjection );\
	Out.vTexCoord = vTexCoord;\
	return Out;\
}\
float g_time;\
float4 PSWater( float2 vTexCoord : TEXCOORD0, float3 vPosition : TEXCOORD1 ):COLOR0{\
	float4 viewPos = mul( float4( vPosition, 1 ), g_matView );\
	float4 projPos = mul( viewPos, g_matProjection );\
	projPos /= projPos.w;\
	projPos.x = ( projPos.x + 1.0f ) / 2.0f;\
	projPos.y = ( 1.0f - projPos.y ) / 2.0f;\
	float4 dcolor = tex2D( t0, projPos.xy );\
	float readDepth = dcolor.g*256.0f*256.0f + dcolor.b*256.0f;\
	float diff = abs( viewPos.z - readDepth );\
	float f = 1.0f / ( 1.0f + diff/500.0f );\
	\
	float2 vTexCoord1 = vTexCoord + float2( g_time/40.0f, g_time/40.0f );\
	float2 vTexCoord2 = vTexCoord + float2( g_time/50.0f, 0 );\
	float h = ( tex2D( t2, vTexCoord1 ).r + tex2D( t2, vTexCoord2 ).r )*0.5f;\
	float3 n = float3(0,0.1,0);\
	float tt = 1/512.0f;\
	n.x = ( tex2D( t2, vTexCoord1+float2(-tt,0) ).r + tex2D( t2, vTexCoord2+float2(-tt,0) ).r )*0.5f;\
	n.x -= ( tex2D( t2, vTexCoord1+float2(tt,0) ).r + tex2D( t2, vTexCoord2+float2(tt,0) ).r )*0.5f;\
	n.z = ( tex2D( t2, vTexCoord1+float2(0,tt) ).r + tex2D( t2, vTexCoord2+float2(0,tt) ).r )*0.5f;\
	n.z -= ( tex2D( t2, vTexCoord1+float2(0,-tt) ).r + tex2D( t2, vTexCoord2+float2(0,-tt) ).r )*0.5f;\
	n = normalize(n);\
	float diffuseLight = saturate(saturate(dot(n, normalize(float3(0,1,1)))+f)+0.2);\
	float specularLight = saturate( pow(diffuseLight*h,4) ) ;\
	projPos.x += h/50.0f;\
	float4 c = tex2Dlod( t1, float4( projPos.xy, 0, 0 ) ) ;\
	float foam = f*0.7;\
	float4 foamColor = ( tex2D( t3, vTexCoord1*2.0f ) + tex2D( t3, vTexCoord2*2.0f) )*0.5f;\
	float4 waterColor = float4(0.3,0.5,1,1)*(1-foam) + foam*foamColor;\
	float4 finalColor = c*f + (1-f)*waterColor;\
	float4 lightColor = finalColor*diffuseLight*(1-specularLight) + specularLight*float4(1,1,1,1);\
	return lightColor;\
}\
struct SKYOUTPUT{\
	float4 vPosition : POSITION0;\
	float2 vTexCoord : TEXCOORD;\
	float3 vWorldPos : TEXCOORD1;\
};\
SKYOUTPUT VSSkyBlend( float3 vInPosition : POSITION0, float2 vInTexCoord : TEXCOORD0 ){\
	SKYOUTPUT Out = ( SKYOUTPUT )0;\
	Out.vWorldPos = mul( float4( vInPosition, 1 ), g_matWorld ).xyz;\
	Out.vPosition = mul( mul( float4( Out.vWorldPos, 1 ), g_matView ), g_matProjection );\
	Out.vTexCoord = vInTexCoord;\
	return Out;\
}\
float4 PSSkyBlend( float2 vTexCoord : TEXCOORD0, float3 vWorldPos : TEXCOORD1 ) : COLOR0{\
	float4 SkyColor = tex2D( t0, vTexCoord );\
	float4 projPos = mul( mul( float4( vWorldPos, 1 ), g_matView ), g_matProjection );\
	projPos /= projPos.w;\
	projPos.x = ( projPos.x + 1.0f ) * 0.5f;\
	projPos.y = ( 1.0f - projPos.y ) * 0.5f;\
	float4 dcolor = tex2D( t1, projPos.xy );\
	float depth = ( dcolor.g*256.0f*256.0f + dcolor.b*256.0f) / 30000.0f;\
	return float4( SkyColor.xyz, saturate( pow(depth,2) ) );\
}\
struct VSOUT_MD5MODEL{\
	float4 vPosition : POSITION0;\
	float2 vTexCoord : TEXCOORD0;\
	float3 vLightDir : TEXCOORD1;\
	float3 vNormal : TEXCOORD2;\
};\
float3 g_lightDir = normalize( float3( 1.0f, 0.5f, 0 ) );\
VSOUT_MD5MODEL VSMD5Model( float3 vInPosition : POSITION0, float2 vInTexCoord : TEXCOORD0, float3 vInNormal : NORMAL0, float3 vInTangent : TANGENT0 ){\
	VSOUT_MD5MODEL Out = ( VSOUT_MD5MODEL )0;\
	Out.vPosition = mul( mul( mul( float4( vInPosition, 1.0f ), g_matWorld ), g_matView ), g_matProjection );\
	Out.vTexCoord = vInTexCoord;\
	Out.vNormal = vInNormal;\
	float3 vT = normalize( vInTangent );\
	float3 vN = normalize( vInNormal );\
	float3 vB = normalize( cross( vN, vT ) );\
	vT = normalize( cross( vB, vN ) );\
	float3x3 matWorld2Tan = { vT.x, vB.x, vN.x,\
							vT.y, vB.y, vN.y,\
							vT.z, vB.z, vN.z};\
							vInNormal = normalize( vInNormal );\
							Out.vLightDir = normalize( mul( g_lightDir, matWorld2Tan ) );\
	return Out;\
}\
float4 PSMD5Model( float2 vTexCoord : TEXCOORD0, float3 vLightDir : TEXCOORD1, float3 vNormal : TEXCOORD2 ):COLOR0{\
	float4 retColor = tex2D( t0, vTexCoord ); \
	float light = 1.0f;\
	float3 normal = normalize( 2.0f * tex2D( t1, vTexCoord ).xyz - 1.0f );\
	light = saturate( saturate( dot( normal, normalize( vLightDir ) ) ) + 0.3f );\
	retColor.rgb *= light;\
	return retColor;\
}\
\
technique techTerrain{\
	pass p0{\
		VertexShader = compile vs_2_0 VSTerrain();\
		PixelShader = compile ps_3_0 PSTerrain();\
	}\
}\
technique techOrthoDepth{\
	pass p0{\
		VertexShader = compile vs_2_0 VSOrthoDepth();\
		PixelShader = compile ps_2_0 PSOrthoDepth();\
	}\
	pass p1{\
		VertexShader = compile vs_2_0 VSOrthoDepth();\
		PixelShader = compile ps_2_0 PSOrthoDepthGrass();\
		CullMode = None;\
	}\
}\
technique techWater{\
	pass p0{\
		VertexShader = compile vs_2_0 VSWater();\
		PixelShader = compile ps_3_0 PSWater();\
	}\
}\
technique techSkyBlend{\
	pass p0{\
		VertexShader = compile vs_2_0 VSSkyBlend();\
		PixelShader = compile ps_2_0 PSSkyBlend();\
		ZEnable = False;\
		AlphaBlendEnable = True;\
		SrcBlend = SrcAlpha;\
		DestBlend = InvSrcAlpha;\
	}\
}\
technique techMD5Model{\
	pass p0{\
		VertexShader = compile vs_2_0 VSMD5Model();\
		PixelShader = compile ps_3_0 PSMD5Model();\
	}\
}\
";

HRESULT CMUTMapWorld::Create(){
	if ( g_mapEffect )
		return S_OK;
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	LPD3DXBUFFER error = 0;
	HRESULT hr = S_OK;
	if ( FAILED( hr = D3DXCreateEffect( pDevice, g_mapEffectData, sizeof(g_mapEffectData), NULL, NULL, D3DXSHADER_DEBUG, 0, &g_mapEffect, &error) ) ) {
		MUTDXErrorTrace( hr, TString((char*)error->GetBufferPointer()) );
		return E_FAIL;
	}

	if ( FAILED( hr = D3DXCreateEffect( pDevice, g_grassEffectData, strlen( g_grassEffectData ), NULL, NULL, D3DXSHADER_DEBUG, 0, &g_grassEffect, &error ) ) ){
		if ( error ){
			MUTDXErrorTrace( hr, TString( (char*)error->GetBufferPointer() ) );
			return E_FAIL;
		}
	}
	
	if ( FAILED( hr = pDevice->CreateTexture( 1024, 1024, 0, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &pDepthTex, NULL ) ) ){
		MUTDXErrorTrace( hr, L"World depth r32f 512x512 failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = pDevice->CreateTexture( 1024, 1024, 0, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &pDepthTexSmall, NULL ) ) ){
		MUTDXErrorTrace( hr, L"World depth r32f 512x512 failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = pDevice->CreateTexture( 1024, 768, 0, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pScreenTex, NULL ) ) ){
		MUTDXErrorTrace( hr, L"World SSAO tex failed" );
		return E_FAIL;
	}
	
	if ( FAILED( hr = pDevice->CreateDepthStencilSurface( 1024, 1024, D3DFMT_D24X8, D3DMULTISAMPLE_NONE,0, TRUE, &pDepthStencil, NULL ) ) ){
		MUTDXErrorTrace( hr, L"World Depthstencil failed" );
		return E_FAIL;
	}

	hSingleTileEvent = CreateEvent( 0, FALSE, TRUE, 0 );
	if ( ! hSingleTileEvent ){
		MUTErrorTrace( L"tileEvent failed" );
		return E_FAIL;
	}

	for ( int i = 0; i < MAPWORLD_CACHE; i++ ){
		tileCache[i].hResEvent = CreateEvent( 0, FALSE, FALSE, 0 );
		if ( ! tileCache[i].hResEvent ){
			MUTErrorTrace( L"tileCache event failed" );
			return E_FAIL;
		}
	}
	return sky.Create();
}